-- UserM
-- Create by weism
-- 玩家管了模块

module("UserM", package.seeall);

-- 世界树配置表
local worldTreeTable = {};

local leftTime = 0;
local LIFE_AUTO_RECOVER = 3600;

-- 内部函数声明
local whenAttribBonus;
local whenLoginOk;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 转换world_tree信息表
    local world_tree_CSV = get_world_tree_CSV();
    for i = 1, #world_tree_CSV do
        local level = world_tree_CSV[i].level;

        -- 解析升级奖励
        worldTreeTable[level] = parseLevelUpBonus(world_tree_CSV[i]);
    end
    world_tree_CSV = {};
end

-- 模块初始化操作
function init()
    loadCsv();

    -- 关注经验奖励事件
    EventMgr.removeAll("UserM");
    EventMgr.register("UserM", event.DO_BONUS, whenAttribBonus);
    EventMgr.register("UserM", event.LOGIN_OK, whenLoginOk);
end

-- 解析升级奖励
function parseLevelUpBonus(row)
    local bonus = row["level_up_bonus"];
    if "" == bonus then
        row["level_up_bonus"] = {};
        return row;
    end

    local arr = string.explode(bonus, "+");
    local bonusList = {};
    for _, str in pairs(arr) do
        if string.startWith(str, "1(") and string.endWith(str, ")") then
            -- 物品奖励
            local temp = string.explode(string.sub(str, 3, -2), ",");
            table.insert(bonusList, {1, tonumber(temp[1]), tonumber(temp[2]) });
        elseif string.startWith(str, "2(") and string.endWith(str, ")") then
            -- 属性奖励
            local temp = string.explode(string.sub(str, 3, -2), ",");
            table.insert(bonusList, {2, temp[1], tonumber(temp[2]) });
        else
            assert(false, "升级奖励类型配置错误");
        end
    end

    row["level_up_bonus"] = bonusList;
    return row;
end

------------------------------------
-- 检索世界树配置
-- @param level 世界树宠物
-- @param path  字段
function queryWorldTree(level, path)
    if not worldTreeTable[level] then
        return nil;
    end

    if path == nil or path == "" then
        return worldTreeTable[level];
    end

    return worldTreeTable[level][path];
end

-- 属性奖励回调
whenAttribBonus = function(bonus)
    -- 如果不是经验奖励，就不用管了
    if bonus[1] ~= 2 or bonus[2] ~= "exp" then
        return;
    end

    local currLevel = ME.user.dbase:query("level", 1);
    local exp = ME.user.dbase:query("exp", 0);
    local level = currLevel;

    repeat
        -- 等级已达上限
        if level >= MAX_USER_LEVEL then
            -- 修正经验
            if exp > queryWorldTree(level, "exp") then
                exp = queryWorldTree(level, "exp");
            end
            break;
        end

        local needExp = queryWorldTree(level, "exp");
        if not needExp then
            break;
        end

        -- 当前经验值没达到需求
        if needExp > exp then
            break;
        end

        exp = exp - needExp;
        level = level + 1;
    until not queryWorldTree(level, "exp");

    -- 如果升级了
    if level > currLevel then
        ME.user.dbase:set("level", level);
        ME.user.dbase:set("exp", exp);
        ME.user.dbase:set("max_money_tip", false);

        -- 执行升级奖励奖励
        for lv = currLevel + 1, level do
            doLevelUpBonus(lv);
        end

        -- 抛出事件
        EventMgr.fire(event.USER_LEVEL_UP, { ["preLevel"] = currLevel, ["level"] = level, });
    end

    -- 抛出事件
    EventMgr.fire(event.USER_EXP_UP);
end

-- 玩家登录成功的回调
function whenLoginOk()
    -- 如果不是验证客户端，则进行一些玩家数据统计处理，否则不处理
    if not isVerifyClient() then
        -- 通知手机端玩家登录成功了
        local account = getGaming3rdAccount() or "";
        local rid = ME.user:getRid();
        local name = ME.user:getName();
        local serverId = getCurrentServerId();
        local level = ME.user.dbase:query("level", 1);
        local serverTime = TimeM.getServerTime();
        local firtLoginTime = ME.user.dbase:query("first_login_time") or serverTime;
        notifyMobileUserLogin(account, rid, name, serverId, serverTime, level, firtLoginTime);

        -- 记录语言版本
        local langRecord = ME.user.dbase:query("lang");
        local lang = getLang();
        if type(langRecord) ~= "string" or langRecord == "" or langRecord ~= lang then
            -- 将初始语言版本通知给服务器，以便日后做数据分析用
            Operation.cmd_record_lang(lang);
        end
    end
end

-- 执行升级奖励
function doLevelUpBonus(level)
    local bonusList = queryWorldTree(level, "level_up_bonus");
    if #bonusList <= 0 then
        return;
    end

    trace("UserM", "玩家升到%d级，执行升级奖励。", level);

    for _, arr in pairs(bonusList) do
        -- 体力需要特殊处理一下
        if arr[2] == "life" then
            BonusM.doBonus({2, "life", ME.user:getMaxLife()}, "level_up");
        else
            BonusM.doBonus(arr, "level_up");
        end
    end
end

----------------------------------
-- 计算升级奖励
-- @param formLevel  当前等级
-- @param toLevel    目标等级
function calcLevelUpBonus(fromLevel, toLevel)
    assert(fromLevel <= toLevel);

    if fromLevel == toLevel then
        return {};
    end

    local ret = {};

    -- 合并奖励
    local function mergeBonus(bonusList)
        if #ret == 0 then
            ret = table.append(ret, bonusList);
            return;
        end

        for _, bonus in pairs(bonusList) do
            for i = 1, #ret do
                if ret[i][1] == bonus[1] and ret[i][2] == bonus[2] then
                    ret[i][3] = ret[i][3] + bonus[3];
                end
            end
        end

    end

    for level = fromLevel + 1, toLevel do
        local bonusList = queryWorldTree(level, "level_up_bonus");
        mergeBonus(bonusList);
    end

    return ret;
end

---------------------------------------------------
-- 获取升级提升效果字符串列表
function getLevelUpStringList(level)
    local preLevel = level - 1;

    -- 弹出提示信息
    local strList = {};
    local preLifeLimit = queryWorldTree(preLevel, "life_limit");
    local lifeLimit = queryWorldTree(level, "life_limit");
    if lifeLimit > preLifeLimit then
        table.insert(strList, string.format(getLocStr("life_limit_improve"), lifeLimit - preLifeLimit));
    end

    local preOfflineLimit = queryWorldTree(preLevel, "offline_produce_time");
    local offlineLimit = queryWorldTree(level, "offline_produce_time");
    if offlineLimit > preOfflineLimit then
        -- 将秒转换为小时
        preOfflineLimit = string.format("%0.1f", preOfflineLimit/3600) ;
        offlineLimit    = string.format("%0.1f", offlineLimit/3600) ;
        table.insert(strList, string.format(getLocStr("offline_produce_time_improve"), offlineLimit - preOfflineLimit));
    end

    -- 计算升级奖励
    -- 这里只是收集奖励数据用于发送提示语
    local bonusList = calcLevelUpBonus(preLevel, level);
    local name, amount, unit;
    for _, bonus in pairs(bonusList) do
        if bonus[1] == 1 then
            -- 物品奖励
            local classId = bonus[2];
            name = ItemM.query(classId, "name");
            amount = bonus[3];
            unit = ItemM.query(classId, "unit");
            table.insert(strList, string.format(getLocStr("level_up_bonus_format"), name, amount));
        elseif bonus[1] == 2 then
            -- 属性奖励
            local attrib = bonus[2];
            name = FieldsM.getFieldName(attrib);
            amount = bonus[3];
            if attrib == "idle_worker_num" then
                table.insert(strList, string.format(getLocStr("level_up_apprentice_bonus_format"), amount));
            else
                table.insert(strList, string.format(getLocStr("level_up_bonus_format"), name, amount));
            end
        end
    end

    -- 升级必定会回满体力，升级已不做体力恢复的奖励
    -- table.insert(strList, getLocStr("level_up_life_bonus_format"));

    return strList;
end

-- 设置活力恢复剩余时间
function setLifeRecoveryLeftTime(t)
    leftTime = t;

    EventMgr.fire(event.RECOVERY_LEFT_TIME);
end

-- 获取活力恢复剩余时间
function getLifeRecoveryLeftTime()
    return leftTime;
end

-- 获取当前活力全恢复剩余时间
function getLifeMaxRecoveryLeftTime()
    local maxLife = ME.user:getMaxLife();
    local life = ME.user.dbase:query("life", 0);

    if maxLife <= life then
        -- 没有恢复时间
        return 0;
    end

    -- 计算剩余时间
    return getLifeRecoveryLeftTime() + (maxLife - life - 1) * LIFE_AUTO_RECOVER;
end

-- 获取活力自动恢复总共需要的时间
function getLifeRecoverTime()
    return LIFE_AUTO_RECOVER;
end


